{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1gpe8GuQPbPv"
      },
      "outputs": [],
      "source": [
        "!pip install fast-pytorch-kmeans kmeans_pytorch text2vec"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "Lfw1zt31RqNs"
      },
      "outputs": [],
      "source": [
        "import torch\n",
        "import numpy as np\n",
        "\n",
        "from kmeans_pytorch import kmeans\n",
        "from text2vec import SentenceModel"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IIw_Sb-0Z4e9"
      },
      "source": [
        "embedding"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 文本编码"
      ],
      "metadata": {
        "id": "e-YygM7JToyA"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "id": "hPWWeJKJZ4OD",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "763de10d-db56-46cc-a8f8-277c7ec0b585"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\u001b[32m2024-03-14 09:30:34.174\u001b[0m | \u001b[34m\u001b[1mDEBUG   \u001b[0m | \u001b[36mtext2vec.sentence_model\u001b[0m:\u001b[36m__init__\u001b[0m:\u001b[36m80\u001b[0m - \u001b[34m\u001b[1mUse device: cuda\u001b[0m\n"
          ]
        }
      ],
      "source": [
        "embedder = SentenceModel()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "id": "7AJ0Ttruapkf"
      },
      "outputs": [],
      "source": [
        "# Corpus with example sentences\n",
        "corpus = [\n",
        "    '花呗更改绑定银行卡',\n",
        "    '我什么时候开通了花呗',\n",
        "    'A man is eating food.',\n",
        "    'A man is eating a piece of bread.',\n",
        "    'The girl is carrying a baby.',\n",
        "    'A man is riding a horse.',\n",
        "    'A woman is playing violin.',\n",
        "    'Two men pushed carts through the woods.',\n",
        "    'A man is riding a white horse on an enclosed ground.',\n",
        "]\n",
        "corpus_embeddings = embedder.encode(corpus)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "id": "r_isb7BVbClX"
      },
      "outputs": [],
      "source": [
        "corpus_embeddings = torch.from_numpy(corpus_embeddings).to('cuda')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2Q7b9JI0bHai",
        "outputId": "0959c2b8-e708-4ebd-b00a-b8b6f2ba51cb"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(torch.Tensor, torch.Size([9, 768]))"
            ]
          },
          "metadata": {},
          "execution_count": 25
        }
      ],
      "source": [
        "type(corpus_embeddings), corpus_embeddings.shape"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "num_class = 3 # 分类类别数"
      ],
      "metadata": {
        "id": "xk-70HADT5XY"
      },
      "execution_count": 38,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 聚类可视化类"
      ],
      "metadata": {
        "id": "3NP3OQL5UAX1"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.decomposition import PCA\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "class KMeansPlot:\n",
        "\n",
        "    def __init__(self, numClass=4, func_type='PCA'):\n",
        "        if func_type == 'PCA':\n",
        "            self.func_plot = PCA(n_components=2)\n",
        "        elif func_type == 'TSNE':\n",
        "            from sklearn.manifold import TSNE\n",
        "            self.func_plot = TSNE(2)\n",
        "        self.numClass = numClass\n",
        "\n",
        "    def plot_cluster(self, result, pos, cluster_centers=None):\n",
        "        plt.figure(2)\n",
        "        Lab = [[] for i in range(self.numClass)]\n",
        "        index = 0\n",
        "        for labi in result:\n",
        "            Lab[labi].append(index)\n",
        "            index += 1\n",
        "        color = ['oy', 'ob', 'og', 'cs', 'ms', 'bs', 'ks', 'ys', 'yv', 'mv', 'bv', 'kv', 'gv', 'y^', 'm^', 'b^', 'k^',\n",
        "                    'g^'] * 3\n",
        "\n",
        "        for i in range(self.numClass):\n",
        "            x1 = []\n",
        "            y1 = []\n",
        "            for ind1 in pos[Lab[i]]:\n",
        "                # print ind1\n",
        "                try:\n",
        "                    y1.append(ind1[1])\n",
        "                    x1.append(ind1[0])\n",
        "                except:\n",
        "                    pass\n",
        "            plt.plot(x1, y1, color[i])\n",
        "\n",
        "        if cluster_centers is not None:\n",
        "            #绘制初始中心点\n",
        "            x1 = []\n",
        "            y1 = []\n",
        "\n",
        "            for ind1 in cluster_centers:\n",
        "                try:\n",
        "                    y1.append(ind1[1])\n",
        "                    x1.append(ind1[0])\n",
        "                except:\n",
        "                    pass\n",
        "\n",
        "            plt.plot(x1, y1, \"rv\") #绘制中心\n",
        "        plt.show()\n",
        "\n",
        "    def plot(self, weight, label, cluster_centers=None):\n",
        "        pos = self.func_plot.fit_transform(weight)\n",
        "        # 高纬的中心点坐标，也经过降纬处理\n",
        "        cluster_centers = self.func_plot.fit_transform(cluster_centers)\n",
        "        self.plot_cluster(list(label), pos, cluster_centers)"
      ],
      "metadata": {
        "id": "7rL2AswRUD-z"
      },
      "execution_count": 39,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## kmeans_pytorch"
      ],
      "metadata": {
        "id": "eTOy96riTv6K"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from kmeans_pytorch import kmeans\n",
        "\n",
        "labels, cluster_centers = kmeans(\n",
        "    X=corpus_embeddings,\n",
        "    num_clusters=num_class,\n",
        "    distance='euclidean',\n",
        "    device=torch.device('cuda:0')\n",
        ")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "oaf6Kfn9Ty0p",
        "outputId": "87aeff8c-e0ab-43ae-bf34-b486954be7d8"
      },
      "execution_count": 60,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "running k-means on cuda:0..\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "[running kmeans]: 2it [00:00, 247.15it/s, center_shift=0.000000, iteration=2, tol=0.000100]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "labels"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "eGvXEFIfVRuH",
        "outputId": "05d24e07-0d75-4987-eae5-89ad92450ed5"
      },
      "execution_count": 61,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([0, 0, 1, 1, 2, 1, 2, 1, 1])"
            ]
          },
          "metadata": {},
          "execution_count": 61
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "cluster_centers"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "d1g8VaZWVfAW",
        "outputId": "216c53d1-1a09-4ade-8ecf-6298898037a4"
      },
      "execution_count": 62,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([[ 0.3271, -0.2714,  0.9217,  ..., -0.6270, -0.1306, -0.0952],\n",
              "        [ 0.2802, -0.2230,  0.1945,  ...,  0.0356, -1.1100,  0.3093],\n",
              "        [ 0.2819, -1.1449,  0.2388,  ..., -0.7908, -1.7649, -0.1390]])"
            ]
          },
          "metadata": {},
          "execution_count": 62
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 结果"
      ],
      "metadata": {
        "id": "1xP3V89eVivc"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class_data = {\n",
        "    i:[]\n",
        "    for i in range(num_class)\n",
        "}\n",
        "\n",
        "for text,cls in zip(corpus, labels):\n",
        "    class_data[cls.item()].append(text)\n",
        "\n",
        "class_data"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "62b3I2MGVge4",
        "outputId": "ccfb4f4d-6ad3-4b03-fc7e-839a80bc3617"
      },
      "execution_count": 63,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{0: ['花呗更改绑定银行卡', '我什么时候开通了花呗'],\n",
              " 1: ['A man is eating food.',\n",
              "  'A man is eating a piece of bread.',\n",
              "  'A man is riding a horse.',\n",
              "  'Two men pushed carts through the woods.',\n",
              "  'A man is riding a white horse on an enclosed ground.'],\n",
              " 2: ['The girl is carrying a baby.', 'A woman is playing violin.']}"
            ]
          },
          "metadata": {},
          "execution_count": 63
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 绘图"
      ],
      "metadata": {
        "id": "SwDqWtGSVxVR"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "k_plot = KMeansPlot(num_class)\n",
        "\n",
        "k_plot.plot(\n",
        "    corpus_embeddings.to('cpu'), # 文本编码的向量\n",
        "    labels.to('cpu'), # 聚类的结果\n",
        "    cluster_centers.to('cpu') # 聚类的高纬中心点，再降纬到2D\n",
        ")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 430
        },
        "id": "MN4gNgnHVzN2",
        "outputId": "a690b0d4-b01b-42ab-a55e-fb20374261a3"
      },
      "execution_count": 64,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## fast_pytorch_kmeans"
      ],
      "metadata": {
        "id": "0j-IGxyKT6pK"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# kmeans\n",
        "# from kmeans_pytorch import kmeans\n",
        "from fast_pytorch_kmeans import KMeans\n",
        "\n",
        "\n",
        "kmeans = KMeans(n_clusters=num_class, mode='euclidean', verbose=1)\n",
        "labels = kmeans.fit_predict(corpus_embeddings)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "lMS1J7bpLg9T",
        "outputId": "eab27197-6552-4b2e-879a-45510f5405ec"
      },
      "execution_count": 51,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "used 2 iterations (0.0039s) to cluster 9 items into 3 clusters\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# 高纬到中心点坐标\n",
        "kmeans.centroids"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "3P9npFrmMKt0",
        "outputId": "453dfc1f-0b78-4fe5-b1db-f513249c30ca"
      },
      "execution_count": 52,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([[ 0.2819, -1.1449,  0.2388,  ..., -0.7908, -1.7649, -0.1390],\n",
              "        [ 0.2802, -0.2230,  0.1945,  ...,  0.0356, -1.1100,  0.3093],\n",
              "        [ 0.3271, -0.2714,  0.9217,  ..., -0.6270, -0.1306, -0.0952]],\n",
              "       device='cuda:0')"
            ]
          },
          "metadata": {},
          "execution_count": 52
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 结果"
      ],
      "metadata": {
        "id": "LCq1VA4xW49p"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class_data = {\n",
        "    i:[]\n",
        "    for i in range(num_class)\n",
        "}\n",
        "\n",
        "for text,cls in zip(corpus, labels):\n",
        "    class_data[cls.item()].append(text)\n",
        "\n",
        "class_data"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6SDyWkxHTXK8",
        "outputId": "773c5889-24fc-4bea-d62a-e076d84c7809"
      },
      "execution_count": 53,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{0: ['The girl is carrying a baby.', 'A woman is playing violin.'],\n",
              " 1: ['A man is eating food.',\n",
              "  'A man is eating a piece of bread.',\n",
              "  'A man is riding a horse.',\n",
              "  'Two men pushed carts through the woods.',\n",
              "  'A man is riding a white horse on an enclosed ground.'],\n",
              " 2: ['花呗更改绑定银行卡', '我什么时候开通了花呗']}"
            ]
          },
          "metadata": {},
          "execution_count": 53
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "k_plot = KMeansPlot(num_class)\n",
        "k_plot.plot(\n",
        "    corpus_embeddings.to('cpu'),\n",
        "    labels.to('cpu'),\n",
        "    kmeans.centroids.to('cpu')\n",
        ")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 430
        },
        "id": "mJDWD10aNxxH",
        "outputId": "2e259ada-c12d-4fb5-991a-ed7e019e94d8"
      },
      "execution_count": 54,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "Dfzlk-YoOE7h"
      },
      "execution_count": null,
      "outputs": []
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "gpuType": "T4",
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}